package com.rock.upms.client.shiro.filter;

import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.AuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import com.rock.common.util.ByteUtil;
import com.rock.common.util.RedisUtil;
import com.rock.common.util.SerializeUtil;
import com.rock.common.util.StringUtil;
import com.rock.upms.client.config.Global;
import com.rock.upms.client.shiro.dto.UsernamePasswordLoginTypeToken;
import com.rock.upms.client.shiro.session.UpmsSessionDao;
import com.rock.upms.client.util.RequestParameterUtil;
import com.rock.upms.model.dto.PermissionDto;
import com.rock.upms.model.dto.RoleDto;
import com.rock.upms.model.dto.UserInfo;
import redis.clients.jedis.Jedis;

/**
 * 重写authc过滤器 Created by hotdog on 2017/3/11.
 */
public class UpmsAuthenticationFilter extends AuthenticationFilter {

	private static final Logger log = LoggerFactory.getLogger(UpmsAuthenticationFilter.class);
	@Autowired
	UpmsSessionDao upmsSessionDao;

	@Override
	protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
		log.trace("--->判断是否已经登录");
		Subject subject = getSubject(request, response);
		// 判断请求类型
		if ("client".equals(Global.getSSOClientType())) {
			boolean clientIs = validateClient(request, response);
			log.trace("--->客户端类型：client，是否已经登录：" + clientIs);
			return clientIs;
		}
		if ("server".equals(Global.getSSOClientType())) {
			boolean isTrue = subject.isAuthenticated();
			log.trace("--->客户端类型：server，是否已经登录：" + isTrue);
			return isTrue;
		}
		log.trace("--->客户端类型：其他，是否已经登录：" + false);
		return false;
	}

	@Override
	protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
		log.trace("--->进行未登录处理");
		StringBuffer ssoServerUrl = new StringBuffer(Global.getSSOServerUrl());
		// server需要登录
		if ("server".equals(Global.getSSOClientType())) {
			ssoServerUrl.append(Global.getAdminPath()).append("/login");
			log.trace("--->客户端类型：server，用户未登录，跳转至sso服务器验证,url:" + ssoServerUrl.toString());
			WebUtils.toHttp(response).sendRedirect(ssoServerUrl.toString());
			return false;
		}
		ssoServerUrl.append(Global.getAdminPath()).append("/login").append("?syscode=").append(Global.getSysCode()).append("&clientType=client");
		if ("third".equals(Global.getSSOType())) {
			// 第三方登录
			ssoServerUrl.append("&loginType=1");
		} else {
			//密码登录
			ssoServerUrl.append("&loginType=0");
		}
		// 回跳地址
		HttpServletRequest httpServletRequest = WebUtils.toHttp(request);
		StringBuffer backurl = httpServletRequest.getRequestURL();
		String queryString = httpServletRequest.getQueryString();
		if (StringUtil.isNotBlank(queryString)) {
			backurl.append("?").append(queryString);
		}
		ssoServerUrl.append("&").append("backurl").append("=").append(URLEncoder.encode(backurl.toString(), "utf-8"));
		log.trace("--->客户端类型：非server，用户未登录，跳转至sso服务器验证url:" + ssoServerUrl.toString());
		WebUtils.toHttp(response).sendRedirect(ssoServerUrl.toString());
		return false;
	}

	/**
	 * 认证中心登录成功带回code
	 * 
	 * @param request
	 */
	private boolean validateClient(ServletRequest request, ServletResponse response) {
		Subject subject = getSubject(request, response);
		Session session = subject.getSession();
		String sessionId = session.getId().toString();
		int timeOut = (int) session.getTimeout() / 1000;
		log.trace("--->开始验证局部会话是否存在");
		// 判断局部会话是否登录
		byte[] tokenCodeByte = RedisUtil.hget(ByteUtil.getByteKey(Global.SSO_CLIENT_SESSIONID, sessionId), "token".getBytes());
		if (tokenCodeByte != null) {
			log.trace("--->局部会话已登录，更新有效期");
			// 更新token有效期
			RedisUtil.expire(ByteUtil.getByteKey(Global.SSO_CLIENT_SESSIONID, sessionId), timeOut);
			return true;
			// // 移除url中的rock_token,rock_usercode参数
			// String backUrl =
			// RequestParameterUtil.getParameterWithOutCode(WebUtils.toHttp(request));
			// HttpServletResponse httpServletResponse =
			// WebUtils.toHttp(response);
			// try {
			// log.trace("--->局部会话已登录，移除token，跳转sendRedirect：" +
			// backUrl.toString());
			// httpServletResponse.sendRedirect(backUrl.toString());
			// return true;
			// } catch (IOException e) {
			// log.error("局部会话已登录，移除code参数跳转出错：", e);
			// return false;
			// }
		}
		// 判断是否有认证中心code
		String token = request.getParameter("rock_token");
		log.trace("--->局部会话不存在，开始验证token：" + token);
		// 将拿到的token到redis中获取，没获取到则需重新登录
		if (StringUtil.isNotBlank(token)) {
			// 判断是否存在token
			Jedis jedis = RedisUtil.getJedis();
			if (jedis.exists(Global.SSO_TOKEN + token)) {
				jedis.close();
				log.trace("--->token存在");
				// 1、初始化客户端session
				log.trace("--->开始初始化client session");
				UserInfo sysUserInfo = getSysUserInfo(token, Global.getSysCode());
				byte[] key = ByteUtil.getByteKey(Global.SSO_CLIENT_SESSIONID, sessionId);
				RedisUtil.hset(key, "token".getBytes(), token.getBytes());
				RedisUtil.hset(key, "userInfo".getBytes(), SerializeUtil.serialize(sysUserInfo));
				RedisUtil.expire(key, timeOut);
				log.trace("--->将客户端client sessionid加入到集合SSO_TOKEN_CLIENT_SESSIONID_SET");
				RedisUtil.sadd(Global.SSO_TOKEN_CLIENT_SESSIONID_SET + token, sessionId);
				// 2、进行无密认证
				// 移除url中的token参数
				String backUrl = RequestParameterUtil.getParameterWithOutCode(WebUtils.toHttp(request));
				// 返回请求资源
				try {
					// client无密认证
					String usercode = request.getParameter("rock_usercode");
					subject.login(new UsernamePasswordLoginTypeToken(usercode, "","0"));
					HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
					log.trace("--->无密认证成功，sendRedirect：" + backUrl.toString());
					httpServletResponse.sendRedirect(backUrl.toString());
					log.trace("--->返回 true");
					return true;
				} catch (IOException e) {
					log.error("--->已拿到code，移除code参数跳转出错：", e);
					return false;
				}
			} else {
				log.trace("---> 全局token在redis中不存在:" + Global.SSO_TOKEN_CLIENT_SESSIONID_SET + token);
				return false;
			}
		} else {
			log.trace("---> 获取token为空:=============");
			return false;
		}
	}

	/**
	 * 功能：从全局session取得客户端userinfo<br>
	 * 
	 * @param token
	 * @param syscode
	 * @return
	 */
	private UserInfo getSysUserInfo(String token, String syscode) {
		if (StringUtil.isBlank(syscode)) {
			log.trace("---> 从全局session抽取客户端userinfo失败：syscode为空");
			return null;
		}
		UserInfo clientUserInfo = new UserInfo();
		// 获取token对应的全局sessionid
		String cacheServerSessionKey = RedisUtil.get(Global.SSO_TOKEN + token);
		byte[] cacheServerUserInfoByte = RedisUtil.hget(cacheServerSessionKey.getBytes(), "userInfo".getBytes());
		UserInfo serverUserInfo = (UserInfo) SerializeUtil.deserialize(cacheServerUserInfoByte);
		clientUserInfo.setUser(serverUserInfo.getUser());// 设置用户基本信息
		// 抽取客户端的角色
		List<RoleDto> serverRoleList = serverUserInfo.getRoleList();
		List<RoleDto> clientRoleList = new ArrayList<>();
		for (RoleDto roleDto : serverRoleList) {
			if (syscode.equals(roleDto.getSystemCode())) {
				clientRoleList.add(roleDto);
			}
		}
		clientUserInfo.setRoleList(clientRoleList);// 设置角色
		// 抽取客户端的权限
		List<PermissionDto> serverPermissionList = serverUserInfo.getPermissionList();
		List<PermissionDto> clientPermissionList = new ArrayList<>();
		for (PermissionDto permissionDto : serverPermissionList) {
			if (syscode.equals(permissionDto.getSystemCode())) {
				clientPermissionList.add(permissionDto);
			}
		}
		clientUserInfo.setPermissionList(clientPermissionList);// 设置权限
		return clientUserInfo;
	}
}
